package com.xh.baselibrary.utils.net;

import com.xh.baselibrary.log.XLog;

class NetPolicy implements PingWorker.NetListener, RequestNetWorker.RequestNetListener {
    public enum NetState {
        WEAK,
        HEALTHY,
        STRONG
    }

    //网络弱判断限制，默认700ms
    private long weakNetDelayLimit;

    private String TAG = NetPolicy.class.getSimpleName();
    //网络延迟检测使用数量 连续三次延迟超过700ms则切换网络状态，默认3次
    private int checkTimes = 3;
    //网络是否有效
    private volatile boolean isNetValid = true;
    //统计样本基于设定次数的三倍扩容
    private int capacity;
    //网络延迟结果统计
    private static final double factor = 0.75;
    private IWeakNetPolicyListener mListener;
    //统计网络状态
    private LimitQueue<Long> dQueue;
    //当前网络状态
    private volatile NetState currNetState = NetState.HEALTHY;

    public NetPolicy(IWeakNetPolicyListener listener, int checks, long weakDelay) {
        mListener = listener;
        checkTimes = checks > 0 ? checks : 3;
        weakNetDelayLimit = weakDelay > 0L ? weakDelay : 700L;
        dQueue = new LimitQueue(capacity);
    }

    public interface IWeakNetPolicyListener {
        void onNetStateChange(NetState netState);
    }

    //切换网络状态
    private void switchNet(NetState netState) {
        if (netState == NetState.WEAK) {
            clearSample();
        }
        if (netState == currNetState) return;
        if (!isNetValid) {
            XLog.i(TAG, " isNetValid false");
            netState = NetState.WEAK;
        }
        notifyNetState(netState);
        currNetState = netState;
    }

    private void notifyNetState(NetState netState) {
        if (mListener != null && netState != null) {
            mListener.onNetStateChange(netState);
        }
        //网络状态切换后清空样本
        clearSample();
    }

    //添加网络延迟数据
    private void addDuration(long delay) {
        if (dQueue == null) {
            return;
        }
        //将delay数据加入对了，超过总数是自动去陈嘉欣
        dQueue.offer(delay);
        if (!dQueue.isEmpty()) {
            NetState state = check(dQueue);
        }
    }

    private NetState check(LimitQueue<Long> queue) {
        double weakTimes = 0.0;
        double healthyTimes = 0d;
        double timeoutTimes = 0d;
        for (long delay : queue) {
            //统计网络次数
            if (delay == PingWorker.CONNECT_TIMEOUT_RESULT) {
                timeoutTimes++;
            } else if (delay > weakNetDelayLimit) {
                weakTimes++;
            } else {
                healthyTimes++;
            }
        }
        NetState state;
        if (queue.size() >= checkTimes) {
            /*
             * 1.超时次数超过队列总数的 75%  or 超时次数达到3次及以上 -> 断网
             * 2.弱网统计次数超过队列总数的 75%  -> 断网
             * 3.有效网络统计次数超过队列总数的 75% & 超时次数为0  -> 稳定网络
             * 4.普通网络
             */
            if ((timeoutTimes / queue.size()) >= factor || timeoutTimes >= checkTimes) {
                state = NetState.WEAK;
            } else if ((weakTimes / queue.size()) >= factor) {
                state = NetState.WEAK;
            } else if ((healthyTimes / queue.size()) >= factor && timeoutTimes == 0) {
                state = NetState.STRONG;
            } else {
                state = NetState.HEALTHY;
            }
        } else {
            /*
             * 当延迟队列只有一到两次统计数据时
             * 1. 判断当前均为正常网络时马上恢复正常网络状态
             * 2. 当timeout达到2次及以上时认为断网
             * 3. 其它情况暂不是更新网络状态, 等待更多数据
             */
            double max = findMax(weakTimes, healthyTimes, timeoutTimes);
            if (max == healthyTimes) {
                state = NetState.HEALTHY;
            } else if (timeoutTimes >= 2) {
                state = NetState.WEAK;
            } else {
                state = null;
            }

        }
        return state;
    }

    private double findMax(double a, double b, double c) {
        return Math.max((Math.max(a, b)), c);
    }

    //清空样本
    private void clearSample() {
        if (dQueue != null) {
            dQueue.clear();
        }
    }

    @Override
    public void onDelay(long log) {
        addDuration(log);
    }

    @Override
    public void onError() {
        switchNet(NetState.WEAK);
    }

    @Override
    public void onNetValid(boolean isValid) {
        XLog.i(TAG,"onNetValid = "+isValid);
        isNetValid =isValid;
    }
}
